package mdata

import (
	"gitee.com/dennis-mxx/gcode-common/mcontext"
	"gitee.com/dennis-mxx/gcode-common/mcore"
	"strings"
	"xorm.io/builder"
)

type pageable[M any] struct {
	inst      M
	ent       Entity
	page      mcore.CmdPage
	handler   XormHandler
	mtx       *mcontext.MContext
	condition any
	convert   func(field string) string
	cols      []string
}

func StartPage[M any](entity Entity, inst M, handler XormHandler, mtx *mcontext.MContext, cmd mcore.CmdPage) *pageable[M] {
	return &pageable[M]{page: cmd, handler: handler, mtx: mtx, inst: inst,
		ent: entity, convert: func(field string) string {
			return field
		}}
}
func (ew *pageable[M]) Sorter(fn func(field string) string) *pageable[M] {
	ew.convert = fn
	return ew
}

func (ew *pageable[M]) Where(condition any) *pageable[M] {
	ew.condition = condition
	return ew
}
func (ew *pageable[M]) Cols(cols []string) *pageable[M] {
	ew.cols = cols
	return ew
}
func (ew *pageable[M]) Find() (*mcore.Pageable, error) {
	if ew.condition == nil {
		ew.condition = builder.Builder{}
	}
	if count, err := ew.handler.Session(ew.mtx).Table(ew.ent).Where(ew.condition).Count(ew.ent); err == nil {

		int64Page := ew.page.GetPage()
		int64Size := ew.page.GetSize()
		remainder := count % int64Size
		maxPage := count / int64Size
		if remainder > 0 {
			maxPage += 1
		}
		result := &mcore.Pageable{
			Page:          int64Page,
			Size:          int64Size,
			MaxPage:       maxPage,
			TotalElements: count,
		}
		if int64Page == 1 {
			result.First = true
		}
		if int64Page >= maxPage {
			result.Last = true
		}

		if int64Page > maxPage {
			result.CurrentElement = 0
			return result, nil
		} else {
			offset := (int64Page - 1) * int64Size
			ses := ew.handler.Session(ew.mtx).Table(ew.ent).Where(ew.condition)
			for i := range ew.page.Sorter {
				arr := strings.Split(ew.page.Sorter[i], ",")
				if len(arr) == 2 {
					if strings.EqualFold("desc", arr[0]) {
						ses.Desc(ew.convert(arr[1]))
					} else {
						ses.Asc(ew.convert(arr[1]))
					}
				}
			}
			if len(ew.cols) > 0 {
				ses.Cols(ew.cols...)
			}

			beanList := make([]M, 0)
			if err := ses.Limit(int(int64Size), int(offset)).Find(&beanList); err == nil {
				result.Content = beanList
				result.CurrentElement = int64(len(beanList))
				return result, nil
			} else {
				return nil, err
			}
		}

	} else {
		return nil, err
	}
}
